package com.flute.icrawler.policy.bdb.storage;

import java.util.concurrent.locks.ReentrantLock;

import org.apache.log4j.Logger;

import com.sleepycat.bind.EntryBinding;
import com.sleepycat.bind.serial.SerialBinding;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.OperationStatus;

public class BdbStorage implements IStorage {
	private static final Logger LOGGER = Logger.getLogger(BdbStorage.class.getName());

	// 数据库环境
	private BdbEnvironment env = null;

	// 数据库
	private Database database = null;

	// 数据绑定
	private EntryBinding<Object> dataBinding = null;

	// lock
	private ReentrantLock lock = null;

	// Bdb name
	private String name;

	/**
	 * 
	 */
	public BdbStorage(String name, BdbEnvironment env) {
		this.name = name;
		this.env = env;
		this.lock = new ReentrantLock();
		this.init();
	}

	public void setDataBinding(EntryBinding<Object> dataBinding) {
		this.dataBinding = dataBinding;
	}

	/**
	 * init bdb
	 * 
	 */
	private void init() {
		try {
			DatabaseConfig dbConfig = new DatabaseConfig();
			dbConfig.setAllowCreate(true);
			dbConfig.setSortedDuplicates(false);
			dbConfig.setDeferredWrite(true);
			database = env.openDatabase(null, this.name, dbConfig);
			dataBinding = new SerialBinding<Object>(env.getClassCatalog(), Object.class);
		} catch (Exception e) {
			LOGGER.error(e);
		}
	}

	/**
	 * colse dbd and release resources
	 * 
	 * @throws DatabaseException
	 */
	public void close() throws DatabaseException {
		if (database != null) {
			database.close();
		}
	}

	@Override
	public long count() {
		this.lock();
		try {
			if (database != null) {
				return database.count();
			}
			return 0;
		} finally {
			this.unlock();
		}
	}

	@Override
	public void put(Object key, Object value) {
		Cursor cursor = null;
		OperationStatus status = null;
		try {
			this.lock();
			cursor = database.openCursor(null, null);
			DatabaseEntry keyEntry = new DatabaseEntry();
			DatabaseEntry valueEntry = new DatabaseEntry();
			dataBinding.objectToEntry(key, keyEntry);
			dataBinding.objectToEntry(value, valueEntry);
			status = cursor.put(keyEntry, valueEntry);
			if (status != OperationStatus.SUCCESS) {
				LOGGER.info("save date error");
			}
			this.sync();
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			if (cursor != null) {
				cursor.close();
			}
			this.unlock();
		}
	}

	@Override
	public Object search(Object key) {
		if (key == null) {
			throw new RuntimeException("The key can't be null");
		}
		this.lock();
		Cursor cursor = null;
		OperationStatus status = null;
		try {
			cursor = database.openCursor(null, null);
			DatabaseEntry keyEntry = new DatabaseEntry();
			DatabaseEntry valueEntry = new DatabaseEntry();
			dataBinding.objectToEntry(key, keyEntry);
			status = cursor.getSearchKey(keyEntry, valueEntry, null);
			if (status != OperationStatus.SUCCESS || valueEntry.getData() == null || valueEntry.getData().length <= 0) {
				return null;
			}
			return dataBinding.entryToObject(valueEntry);
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			if (cursor != null) {
				cursor.close();
			}
			this.unlock();
		}
	}

	@Override
	public Object getFirst() {
		Cursor cursor = null;
		OperationStatus status = null;
		try {
			DatabaseEntry key = new DatabaseEntry();
			DatabaseEntry value = new DatabaseEntry();
			cursor = database.openCursor(null, null);
			status = cursor.getFirst(key, value, null);
			if (status != OperationStatus.SUCCESS || key.getData() == null || key.getData().length < 1
					|| value.getData() == null || value.getData().length < 1) {
				return null;
			}
			return dataBinding.entryToObject(value);
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			if (cursor != null) {
				cursor.close();
			}
		}
	}

	@Override
	public void delete(Object key) {
		OperationStatus status = null;
		try {
			// this.lock();
			DatabaseEntry keyEntry = new DatabaseEntry();
			dataBinding.objectToEntry(key, keyEntry);
			status = database.delete(null, keyEntry);
			if (status == OperationStatus.SUCCESS) {
				LOGGER.info("delete data success! key=" + name);
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			// this.unlock();
		}
	}

	@Override
	public Object next() {
		return null;
	}

	@Override
	public Object next(Object primaryKey) {
		Cursor cursor = null;
		OperationStatus status = null;
		try {
			this.lock();
			DatabaseEntry keyEntry = new DatabaseEntry();
			DatabaseEntry valueEntry = new DatabaseEntry();
			cursor = database.openCursor(null, null);
			if (primaryKey != null) {
				dataBinding.objectToEntry(primaryKey, keyEntry);
				status = cursor.getSearchKey(keyEntry, valueEntry, null);
				if (status != OperationStatus.SUCCESS) {
					return null;
				}
			}
			status = cursor.getNext(keyEntry, valueEntry, null);
			if (status != OperationStatus.SUCCESS) {
				return null;
			}
			return dataBinding.entryToObject(valueEntry);
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			if (cursor != null) {
				try {
					cursor.close();
				} catch (DatabaseException e) {
				}
			}
			this.unlock();
		}
	}

	@Override
	public void lock() {
		lock.lock();
	}

	@Override
	public void unlock() {
		lock.unlock();
	}

	@Override
	public boolean isContains(Object key) {
		if (this.search(key) != null) {
			return true;
		}
		return false;
	}

	/**
	 * 取得数据名称
	 */
	public String getName() {
		return this.name;
	}

	@Override
	public void sync() {
		database.sync();
	}
}
